package cn.iocoder.yudao.module.system.service.userretention;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.userretention.vo.UserRetentionPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.userretention.vo.UserRetentionRespVO;
import cn.iocoder.yudao.module.system.controller.admin.userretention.vo.UserRetentionStatsRespVO;
import cn.iocoder.yudao.module.system.convert.userretention.UserRetentionConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.userretention.UserRetentionStatsDO;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.dal.mysql.userretention.UserRetentionStatsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 用户留存率计算服务实现
 *
 * @author 系统
 */
@Service
@Slf4j
public class UserRetentionServiceImpl implements UserRetentionService {

    @Resource
    private UserRetentionStatsMapper retentionStatsMapper;

    @Resource
    private AdminUserMapper adminUserMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void calculateRetentionRate(LocalDate targetDate) {
        log.info("开始计算留存率，目标日期：{}", targetDate);
        
        try {
            // 获取目标日期需要计算留存率的记录
            List<UserRetentionStatsDO> pendingRecords = retentionStatsMapper.selectPendingCalculationByDate(targetDate);
            
            if (pendingRecords.isEmpty()) {
                log.info("目标日期 {} 没有需要计算留存率的记录", targetDate);
                return;
            }
            
            log.info("目标日期 {} 共有 {} 条记录需要计算留存率", targetDate, pendingRecords.size());
            
            // 批量计算留存率
            for (UserRetentionStatsDO record : pendingRecords) {
                try {
                    calculateSingleUserRetention(record);
                } catch (Exception e) {
                    log.error("计算用户留存率失败，记录ID：{}，用户ID：{}", record.getId(), record.getUserId(), e);
                }
            }
            
            log.info("完成留存率计算，目标日期：{}，处理记录数：{}", targetDate, pendingRecords.size());
            
        } catch (Exception e) {
            log.error("计算留存率失败，目标日期：{}", targetDate, e);
            throw e;
        }
    }

    @Override
    public void batchCalculateRetentionRate(LocalDate startDate, LocalDate endDate) {
        log.info("开始批量计算留存率，日期范围：{} 到 {}", startDate, endDate);
        
        LocalDate currentDate = startDate;
        while (!currentDate.isAfter(endDate)) {
            try {
                calculateRetentionRate(currentDate);
            } catch (Exception e) {
                log.error("批量计算留存率失败，日期：{}", currentDate, e);
            }
            currentDate = currentDate.plusDays(1);
        }
        
        log.info("完成批量留存率计算，日期范围：{} 到 {}", startDate, endDate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void calculateUserRetentionRate(Long userId, LocalDate usageDate) {
        log.info("开始计算单个用户留存率，用户ID：{}，使用日期：{}", userId, usageDate);
        
        try {
            List<UserRetentionStatsDO> userRecords = retentionStatsMapper.selectByUserIdAndDate(userId, usageDate);
            
            for (UserRetentionStatsDO record : userRecords) {
                calculateSingleUserRetention(record);
            }
            
            log.info("完成单个用户留存率计算，用户ID：{}，使用日期：{}", userId, usageDate);
            
        } catch (Exception e) {
            log.error("计算单个用户留存率失败，用户ID：{}，使用日期：{}", userId, usageDate, e);
            throw e;
        }
    }

    @Override
    public PageResult<UserRetentionRespVO> getUserRetentionPage(UserRetentionPageReqVO pageReqVO) {
        PageResult<UserRetentionStatsDO> pageResult = retentionStatsMapper.selectPage(pageReqVO);
        return UserRetentionConvert.INSTANCE.convertPage(pageResult);
    }

    @Override
    public UserRetentionStatsRespVO getUserRetentionStats() {
        UserRetentionStatsRespVO stats = new UserRetentionStatsRespVO();
        
        // 获取总用户数
        long totalUsers = retentionStatsMapper.countTotalUsers(null, null);
        stats.setTotalUsers(totalUsers);
        
        if (totalUsers > 0) {
            // 计算各期留存率
            long retention3Day = retentionStatsMapper.countRetentionUsers(null, null, 3);
            long retention7Day = retentionStatsMapper.countRetentionUsers(null, null, 7);
            long retention15Day = retentionStatsMapper.countRetentionUsers(null, null, 15);
            
            stats.setRetention3DayRate((double) retention3Day / totalUsers * 100);
            stats.setRetention7DayRate((double) retention7Day / totalUsers * 100);
            stats.setRetention15DayRate((double) retention15Day / totalUsers * 100);
            
            stats.setRetention3DayUsers(retention3Day);
            stats.setRetention7DayUsers(retention7Day);
            stats.setRetention15DayUsers(retention15Day);
            
            // 计算平均登录天数和活跃用户数
            Double avgLoginDays = retentionStatsMapper.getAverageLoginDays(null, null);
            stats.setAverageLoginDays(avgLoginDays != null ? avgLoginDays : 0.0);
            
            long activeUsers = retentionStatsMapper.countActiveUsers(null, null);
            stats.setActiveUsers(activeUsers);
            stats.setChurnedUsers(totalUsers - activeUsers);
        }
        
        return stats;
    }

    @Override
    public UserRetentionRespVO getUserRetention(Long id) {
        UserRetentionStatsDO retentionStats = retentionStatsMapper.selectById(id);
        return UserRetentionConvert.INSTANCE.convert(retentionStats);
    }

    @Override
    public void calculateUserRetention(Long userId) {
        calculateUserRetentionRate(userId, LocalDate.now());
    }

    @Override
    public void calculateUserRetentionBatch(List<Long> userIds) {
        for (Long userId : userIds) {
            calculateUserRetention(userId);
        }
    }

    @Override
    public void recalculateRetention(LocalDate startDate, LocalDate endDate) {
        batchCalculateRetentionRate(startDate, endDate);
    }

    @Override
    public int deleteExpiredData(LocalDate beforeDate) {
        return deleteExpiredRetentionStats(beforeDate);
    }

    @Override
    public double getCalculationProgress() {
        return getCalculationProgress(LocalDate.now());
    }

    @Override
    public List<UserRetentionStatsDO> getUserRetentionDetails(Long userId, LocalDate startDate, LocalDate endDate) {
        return retentionStatsMapper.selectByUserId(userId, startDate, endDate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteExpiredRetentionStats(LocalDate beforeDate) {
        log.info("开始删除过期留存率统计数据，删除日期：{} 之前的数据", beforeDate);
        
        int deletedCount = retentionStatsMapper.deleteByDateBefore(beforeDate);
        
        log.info("完成删除过期留存率统计数据，删除记录数：{}", deletedCount);
        
        return deletedCount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recalculateUserRetentionRate(Long userId, Long usageLogId) {
        log.info("开始重新计算用户留存率，用户ID：{}，使用记录ID：{}", userId, usageLogId);
        
        try {
            UserRetentionStatsDO record = retentionStatsMapper.selectByUserIdAndUsageLogId(userId, usageLogId);
            
            if (record == null) {
                log.warn("未找到留存率统计记录，用户ID：{}，使用记录ID：{}", userId, usageLogId);
                return;
            }
            
            // 重置计算状态
            record.setRetentionStatus(0); // 0-计算中
            record.setRetention3Days(null);
            record.setRetention7Days(null);
            record.setRetention15Days(null);
            record.setRetention3DaysLoginTime(null);
            record.setRetention7DaysLoginTime(null);
            record.setRetention15DaysLoginTime(null);
            record.setCalculatedTime(null);
            record.setTotalLoginDays(0);
            record.setConsecutiveLoginDays(0);
            record.setLastLoginTime(null);
            
            retentionStatsMapper.updateById(record);
            
            // 重新计算
            calculateSingleUserRetention(record);
            
            log.info("完成重新计算用户留存率，用户ID：{}，使用记录ID：{}", userId, usageLogId);
            
        } catch (Exception e) {
            log.error("重新计算用户留存率失败，用户ID：{}，使用记录ID：{}", userId, usageLogId, e);
            throw e;
        }
    }

    @Override
    public long getPendingCalculationCount() {
        return retentionStatsMapper.countPendingCalculation();
    }

    @Override
    public double getCalculationProgress(LocalDate targetDate) {
        long totalCount = retentionStatsMapper.countByDate(targetDate);
        long completedCount = retentionStatsMapper.countCompletedByDate(targetDate);
        
        if (totalCount == 0) {
            return 100.0;
        }
        
        return (double) completedCount / totalCount * 100;
    }

    /**
     * 计算单个用户的留存率
     */
    private void calculateSingleUserRetention(UserRetentionStatsDO record) {
        log.debug("开始计算单个用户留存率，记录ID：{}，用户ID：{}", record.getId(), record.getUserId());
        
        try {
            LocalDate usageDate = record.getCodeUsageDate();
            LocalDate now = LocalDate.now();
            
            // 计算3日留存
            LocalDate retention3Date = usageDate.plusDays(3);
            if (!retention3Date.isAfter(now)) {
                boolean hasLogin3Days = checkUserLoginOnDate(record.getUserId(), retention3Date);
                record.setRetention3Days(hasLogin3Days);
                if (hasLogin3Days) {
                    record.setRetention3DaysLoginTime(getLastLoginTimeOnDate(record.getUserId(), retention3Date));
                }
            }
            
            // 计算7日留存
            LocalDate retention7Date = usageDate.plusDays(7);
            if (!retention7Date.isAfter(now)) {
                boolean hasLogin7Days = checkUserLoginOnDate(record.getUserId(), retention7Date);
                record.setRetention7Days(hasLogin7Days);
                if (hasLogin7Days) {
                    record.setRetention7DaysLoginTime(getLastLoginTimeOnDate(record.getUserId(), retention7Date));
                }
            }
            
            // 计算15日留存
            LocalDate retention15Date = usageDate.plusDays(15);
            if (!retention15Date.isAfter(now)) {
                boolean hasLogin15Days = checkUserLoginOnDate(record.getUserId(), retention15Date);
                record.setRetention15Days(hasLogin15Days);
                if (hasLogin15Days) {
                    record.setRetention15DaysLoginTime(getLastLoginTimeOnDate(record.getUserId(), retention15Date));
                }
            }
            
            // 计算总登录天数和连续登录天数
            calculateLoginStats(record, usageDate, now);
            
            // 更新计算状态
            record.setRetentionStatus(1); // 1-已计算
            record.setCalculatedTime(LocalDateTime.now());
            
            // 保存更新
            retentionStatsMapper.updateById(record);
            
            log.debug("完成单个用户留存率计算，记录ID：{}，用户ID：{}", record.getId(), record.getUserId());
            
        } catch (Exception e) {
            log.error("计算单个用户留存率失败，记录ID：{}，用户ID：{}", record.getId(), record.getUserId(), e);
            
            // 标记计算失败
            record.setRetentionStatus(2); // 2-计算失败
            record.setCalculatedTime(LocalDateTime.now());
            retentionStatsMapper.updateById(record);
            
            throw e;
        }
    }

    /**
     * 检查用户在指定日期是否有登录
     */
    private boolean checkUserLoginOnDate(Long userId, LocalDate date) {
        try {
            // 这里需要根据实际的用户登录记录表来查询
            // 暂时使用用户的最后登录时间来模拟
            AdminUserDO user = adminUserMapper.selectById(userId);
            if (user == null || user.getLoginDate() == null) {
                return false;
            }
            
            LocalDate loginDate = user.getLoginDate().toLocalDate();
            return !loginDate.isBefore(date);
            
        } catch (Exception e) {
            log.warn("检查用户登录状态失败，用户ID：{}，日期：{}", userId, date, e);
            return false;
        }
    }

    /**
     * 获取用户在指定日期的最后登录时间
     */
    private LocalDateTime getLastLoginTimeOnDate(Long userId, LocalDate date) {
        try {
            AdminUserDO user = adminUserMapper.selectById(userId);
            if (user == null || user.getLoginDate() == null) {
                return null;
            }
            
            LocalDate loginDate = user.getLoginDate().toLocalDate();
            if (!loginDate.isBefore(date)) {
                return user.getLoginDate();
            }
            
            return null;
            
        } catch (Exception e) {
            log.warn("获取用户登录时间失败，用户ID：{}，日期：{}", userId, date, e);
            return null;
        }
    }

    /**
     * 计算登录统计信息
     */
    private void calculateLoginStats(UserRetentionStatsDO record, LocalDate usageDate, LocalDate now) {
        try {
            Long userId = record.getUserId();
            
            // 这里需要根据实际的用户登录记录表来统计
            // 暂时使用简单的逻辑
            AdminUserDO user = adminUserMapper.selectById(userId);
            if (user != null && user.getLoginDate() != null) {
                record.setLastLoginTime(user.getLoginDate());
                
                // 简单计算总登录天数（从使用日期到现在的天数）
                long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(usageDate, now);
                record.setTotalLoginDays((int) Math.min(daysBetween, 15)); // 最多统计15天
                
                // 简单计算连续登录天数
                LocalDate loginDate = user.getLoginDate().toLocalDate();
                long consecutiveDays = java.time.temporal.ChronoUnit.DAYS.between(usageDate, loginDate);
                record.setConsecutiveLoginDays((int) Math.max(0, Math.min(consecutiveDays, 15)));
            } else {
                record.setTotalLoginDays(0);
                record.setConsecutiveLoginDays(0);
            }
            
        } catch (Exception e) {
            log.warn("计算登录统计信息失败，用户ID：{}", record.getUserId(), e);
            record.setTotalLoginDays(0);
            record.setConsecutiveLoginDays(0);
        }
    }
}